package com.open.capacity.workflow.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.open.capacity.commons.CodeEnum;
import com.open.capacity.commons.PageResult;
import com.open.capacity.commons.Result;
import com.open.capacity.model.system.LoginAppUser;
import com.open.capacity.model.system.SysUser;
import com.open.capacity.finance.util.uuid.UUIDUtils;
import com.open.capacity.user.model.SysCompany;
import com.open.capacity.user.model.SysDepartment;
import com.open.capacity.user.service.SysCompanyService;
import com.open.capacity.user.service.SysDepartmentService;
import com.open.capacity.utils.IdGenerator;
import com.open.capacity.utils.MapUtil;
import com.open.capacity.utils.PageUtil;
import com.open.capacity.utils.SysUserUtil;
import com.open.capacity.workflow.dao.WFDelegateRuleDao;
import com.open.capacity.workflow.dao.WFDynamicScriptDao;
import com.open.capacity.workflow.dao.WFProcessinstanceDao;
import com.open.capacity.workflow.model.WFDelegateRule;
import com.open.capacity.workflow.model.WFProcessinstance;
import com.open.capacity.workflow.model.WFTask;
import com.open.capacity.workflow.service.WFProcessinstanceService;
import com.open.capacity.workflow.service.WFTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.map.CaseInsensitiveMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;


@Slf4j
@Service
public class WFProcessinstanceServiceImpl implements WFProcessinstanceService {

    @Autowired
    private SysCompanyService companyService;

    @Autowired
    private SysDepartmentService departmentService;

    @Autowired
    private WFProcessinstanceDao processinstanceDao;

    @Autowired
    private WFDelegateRuleDao delegateRuleDao;

    @Autowired
    private WFDynamicScriptDao dynamicScriptDao;

    @Autowired
    private WFTaskService taskService;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(WFProcessinstance processinstance) {
        processinstance.setCode("P" + IdGenerator.next("process", 6)); //TODO 前缀后期可以配置
        SysCompany company = companyService.findById(processinstance.getCompanyId());
        SysDepartment department = departmentService.findById(processinstance.getDepartmentId());
        processinstance.setCompanyName(company.getShortName());
        processinstance.setDepartmentName(department.getName());
        processinstanceDao.save(processinstance);
        log.info("保存流程实例：{}", processinstance);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(WFProcessinstance processinstance) {
        SysCompany company = companyService.findById(processinstance.getCompanyId());
        SysDepartment department = departmentService.findById(processinstance.getDepartmentId());
        processinstance.setCompanyName(company.getShortName());
        processinstance.setDepartmentName(department.getName());
        processinstanceDao.updateByOps(processinstance);
        log.info("修改流程实例：{}", processinstance);
    }

    @Override
    public void updateStatus(String id, Integer status) {
        processinstanceDao.updateStatus(id, status);
        log.info("撤销流程实例：{}", id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revoke(String id, Integer status) {
        processinstanceDao.updateStatus(id, status);
        taskService.deleteByProcessIdNoHistory(id);
        log.info("撤销流程实例：{}", id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(String id) {
        WFProcessinstance processinstance = processinstanceDao.findById(id);
        taskService.deleteByProcessId(id);
        processinstanceDao.delete(id);

        log.info("删除流程实例：{}", processinstance);
    }


    @Override
    public WFProcessinstance findById(String id) {
        return processinstanceDao.findById(id);
    }

    @Override
    public List<WFProcessinstance> findProcessinstances(Map<String, Object> params) {
//        //设置分页信息，分别是当前页数和每页显示的总记录数【记住：必须在mapper接口中的方法执行之前设置该分页信息】
//        if (MapUtils.getInteger(params, "page") != null && MapUtils.getInteger(params, "limit") != null)
//            PageHelper.startPage(MapUtils.getInteger(params, "page"), MapUtils.getInteger(params, "limit"), true);
        List<WFProcessinstance> list = processinstanceDao.findList(params);
        list.forEach(item -> {
            List<WFTask> tasks = taskService.findTasksByProcessId(item.getId());
            if (tasks.stream().filter(t -> t.getIsFinished() == 1).findFirst().isPresent()) {
                item.setIsStart(1);
            } else {
                item.setIsStart(0);
            }
        });
        return list;
    }

    @Override
    public PageResult<WFProcessinstance> pageProcessinstances(Map<String, Object> params) {
        //设置分页信息，分别是当前页数和每页显示的总记录数【记住：必须在mapper接口中的方法执行之前设置该分页信息】
        if (org.apache.commons.collections.MapUtils.getInteger(params, "page") != null && org.apache.commons.collections.MapUtils.getInteger(params, "limit") != null)
            PageHelper.startPage(org.apache.commons.collections.MapUtils.getInteger(params, "page"), org.apache.commons.collections.MapUtils.getInteger(params, "limit"), true);

        List<WFProcessinstance> list = processinstanceDao.findList(params);
        list.forEach(item -> {
            List<WFTask> tasks = taskService.findTasksByProcessId(item.getId());
            if (tasks.stream().filter(t -> t.getIsFinished() == 1).findFirst().isPresent()) {
                item.setIsStart(1);
            } else {
                item.setIsStart(0);
            }
        });
        PageInfo<WFProcessinstance> pageInfo = new PageInfo(list);

        return PageResult.<WFProcessinstance>builder().data(pageInfo.getList()).resp_code(CodeEnum.SUCCESS.getCode()).count(pageInfo.getTotal()).build();

    }


    @Override
    public Result saveOrUpdate(WFProcessinstance processinstance, String keyValue) {
        int i = 0;
        LoginAppUser currentUser = SysUserUtil.getLoginAppUser();
        if (StringUtils.isNotEmpty(keyValue)) {
            WFProcessinstance model = processinstanceDao.findById(keyValue);
            if (model != null) {
                if (processinstance.getIsFinished() != null) {
                    model.setIsFinished(processinstance.getIsFinished());
                }
                if (processinstance.getIsAgain() != null) {
                    model.setIsAgain(processinstance.getIsAgain());
                }
                if (processinstance.getProcessLevel() != null) {
                    model.setProcessLevel(processinstance.getProcessLevel());
                }
                if (StringUtils.isNotEmpty(processinstance.getDescription())) {
                    model.setDescription(processinstance.getDescription());
                }

                i = processinstanceDao.updateByOps(model);
            }
        } else {
            processinstance.setId(UUIDUtils.getOriginalUUIDWithoutBar());
            processinstance.setCode("P" + IdGenerator.next("process", 6)); //TODO 前缀后期可以配置
            processinstance.setCreateTime(new Date());
            processinstance.setCreateUserid(currentUser.getId());
            processinstance.setCreateUsername(currentUser.getUsername());
            processinstance.setStatus(1);
            processinstance.setIsFinished(0);
            i = processinstanceDao.save(processinstance);
        }

        return i > 0 ? Result.succeed("操作成功") : Result.failed("操作失败");
    }


    @Override
    public PageResult<WFProcessinstance> pageActiveTasks(LoginAppUser userInfo, Map<String, Object> params) {
        List<SysUser> delegateList = getDelegateTask(userInfo.getId());
        String userId = userInfo.getId();
        String postIds = StringUtils.isEmpty(userInfo.getPostIds()) ? "" : "'" + userInfo.getPostIds().replace(",", "','") + "'";
        String roleIds = StringUtils.isEmpty(userInfo.getRoleIds()) ? "" : "'" + userInfo.getRoleIds().replace(",", "','") + "'";
        String companyId = userInfo.getCompanyId();
        String departmentId = userInfo.getDepartmentId();
        String schemeCode = params.containsKey("schemeCode") && params.get("schemeCode") != null ? params.get("schemeCode").toString() : "";

        // 获取委托信息
        StringBuilder strSql = new StringBuilder();
        strSql.append("select a.task_id as taskId,a.task_type as taskType,a.task_name as taskName,p.id,p.code, p.scheme_id as schemeId,p.scheme_code as schemeCode,p.scheme_name as schemeName,p.status,p.process_name as processName,p.process_level as processLevel,p.is_finished as isFinished,p.is_again as isAgain,p.description,p.create_time as createTime,p.create_userId as createUserid,p.create_userName as createUsername,p.company_id as companyId,p.company_name as companyName,p.department_id as departmentId,p.department_name as departmentName, p.is_childflow as isChildflow,p.parent_id as parentId ")
                .append("from (select max(t.id) as task_id,max(t.task_type )as task_type,t.process_id,t.node_name as task_name from wf_task t where t.is_finished = 0 and (auditor_id = #{auditorId} ");

        if (StringUtils.isNotEmpty(userInfo.getPostIds())) {
            strSql.append("  or (t.auditor_id in (" + postIds + ") and t.company_id = '1' and t.department_id = '1' ) ");
            strSql.append("  or (t.auditor_id in (" + postIds + ") and t.company_id = #{companyId} ) ");
            strSql.append("  or (t.auditor_id in (" + postIds + ") and t.department_id = #{departmentId} ) ");
        }
        if (StringUtils.isNotEmpty(userInfo.getRoleIds())) {
            strSql.append("  or (t.auditor_id in (" + roleIds + ") and t.company_id = '1' and t.department_id = '1' ) ");
            strSql.append("  or (t.auditor_id in (" + roleIds + ") and t.company_id = #{companyId} ) ");
            strSql.append("  or (t.auditor_id in (" + roleIds + ") and t.department_id = #{departmentId}) ");
        }
        // 添加委托信息
        for (SysUser item : delegateList) {
            String processId = "'" + item.getWfProcessId().replace(",", "','") + "'";
            String postIds2 = "'" + item.getPostIds().replace(",", "','") + "'";
            String roleIds2 = "'" + item.getRoleIds().replace(",", "','") + "'";
            String userI2 = "'" + item.getId() + "'";
            String companyId2 = "'" + item.getCompanyId() + "'";
            String departmentId2 = "'" + item.getDepartmentId() + "'";

            strSql.append("  or (t.auditor_id =" + userI2 + " and t.process_id in (" + processId + ") )");

            if (StringUtils.isNotEmpty(item.getPostIds())) {
                strSql.append("  or (t.auditor_id in (" + postIds2 + ") and t.company_id = '1' and t.department_id = '1' and t.process_id in (" + processId + ")  ) ");
                strSql.append("  or (t.auditor_id in (" + postIds2 + ") and t.company_id = " + companyId2 + " AND t.process_id in (" + processId + ") ) ");
                strSql.append("  or (t.auditor_id in (" + postIds2 + ") and t.department_id = " + departmentId2 + " AND t.process_id in (" + processId + ") ) ");
            }

            if (StringUtils.isNotEmpty(item.getRoleIds())) {
                strSql.append("  or (t.auditor_id in (" + roleIds2 + ") and t.company_id = '1' and t.department_id = '1' and t.process_id in (" + processId + ")  ) ");
                strSql.append("  or (t.auditor_id in (" + roleIds2 + ") and t.company_id = " + companyId2 + " and t.process_id in (" + processId + ") ) ");
                strSql.append("  or (t.auditor_id in (" + roleIds2 + ") and t.department_id = " + departmentId2 + " and t.process_id in (" + processId + ") ) ");
            }
        }

        strSql.append(" ) group by t.process_id,t.node_id,t.node_name ) a left join wf_processinstance p on p.id = a.process_id where 1 = 1  and (p.is_finished = 0 or a.task_type = 3) and p.status = 1 ");

        if (params.get("startTime") != null && params.get("endTime") != null) {
            strSql.append(" and ( p.create_time >= #{startTime} and  p.create_time <= #{endTime} ) ");
        }
        if (params.get("keyword") != null && StringUtils.isNotEmpty(params.get("keyword").toString())) {
            strSql.append(" and ( p.process_name like #{keyword} or p.scheme_name like #{keyword}  or p.code like #{keyword} or p.description like #{keyword} ) ");
        }
        strSql.append(" and (p.is_again!=1 or p.is_again is null)");
        if (StringUtils.isNotEmpty(schemeCode)) {
            strSql.append(" and p.scheme_code='" + schemeCode + "' ");
        }
        strSql.append(" order by p.code desc ");

        params.put("auditorId", userId);
        params.put("companyId", companyId);
        params.put("departmentId", departmentId);
        params.put("sql", strSql.toString());

        int total = dynamicScriptDao.count(params);
        List<CaseInsensitiveMap<String, Object>> list;
        List<WFProcessinstance> modelList = new ArrayList<>();
        if (total > 0) {
            PageUtil.pageParamConver(params, true);
            list = dynamicScriptDao.pageList(params);
            try {
                for (Map<String, Object> map : list) {
                    WFProcessinstance item = MapUtil.mapToObject(map, WFProcessinstance.class);
                    modelList.add(item);
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
        }
        return PageResult.<WFProcessinstance>builder().data(modelList).resp_code(CodeEnum.SUCCESS.getCode()).count((long) total).build();
    }

    @Override
    public PageResult<WFProcessinstance> pageFinishedTasks(LoginAppUser user, Map<String, Object> params) {
        params.put("updateUserid", user.getId());
        //设置分页信息，分别是当前页数和每页显示的总记录数【记住：必须在mapper接口中的方法执行之前设置该分页信息】
        if (MapUtils.getInteger(params, "page") != null && MapUtils.getInteger(params, "limit") != null)
            PageHelper.startPage(MapUtils.getInteger(params, "page"), MapUtils.getInteger(params, "limit"), true);

        List<WFProcessinstance> list = processinstanceDao.findFinishedList(params);
        list.forEach(item -> {
            WFTask curTask = taskService.findById(item.getTaskId());
            List<WFTask> nextTasks = taskService.findTasksByProcessIdAndPreviousId(item.getId(), curTask.getNodeId());
            if (nextTasks != null && CollectionUtils.isNotEmpty(nextTasks) && nextTasks.stream().filter(t -> t.getIsFinished() == 1).count() > 0) {
                item.setCanRevoke(0);
            } else {
                item.setCanRevoke(1);
            }
        });
        PageInfo<WFProcessinstance> pageInfo = new PageInfo(list);

        return PageResult.<WFProcessinstance>builder().data(pageInfo.getList()).resp_code(CodeEnum.SUCCESS.getCode()).count(pageInfo.getTotal()).build();
    }

    @Override
    public Boolean hasPermission(String processinstanceId, LoginAppUser userInfo) {
        List<SysUser> delegateList = getDelegateTask(userInfo.getId());
        String userId = userInfo.getId();
        String postIds = StringUtils.isEmpty(userInfo.getPostIds()) ? "" : "'" + userInfo.getPostIds().replace(",", "','") + "'";
        String roleIds = StringUtils.isEmpty(userInfo.getRoleIds()) ? "" : "'" + userInfo.getRoleIds().replace(",", "','") + "'";
        String companyId = userInfo.getCompanyId();
        String departmentId = userInfo.getDepartmentId();

        // 获取委托信息
        StringBuilder strSql = new StringBuilder();
        strSql.append("select a.task_id as taskId,a.task_type as taskType,a.task_name as taskName,p.id,p.code, p.scheme_id as schemeId,p.scheme_code as schemeCode,p.scheme_name as schemeName,p.status,p.process_name as processName,p.process_level as processLevel,p.is_finished as isFinished,p.is_again as isAgain,p.description,p.create_time as createTime,p.create_userId as createUserid,p.create_userName as createUsername,p.company_id as companyId,p.department_id as departmentId,p.is_childflow as isChildflow,p.parent_id as parentId ")
                .append("from (select max(t.id) as task_id,max(t.task_type )as task_type,t.process_id,t.node_name as task_name from wf_task t where t.is_finished = 0 and (t.auditor_id = '1' or  auditor_id = #{auditorId} ");

        if (StringUtils.isNotEmpty(userInfo.getPostIds())) {
            strSql.append("  or (t.auditor_id in (" + postIds + ") and t.company_id = '1' and t.department_id = '1' ) ");
            strSql.append("  or (t.auditor_id in (" + postIds + ") and t.company_id = #{companyId} ) ");
            strSql.append("  or (t.auditor_id in (" + postIds + ") and t.department_id = #{departmentId} ) ");
        }
        if (StringUtils.isNotEmpty(userInfo.getRoleIds())) {
            strSql.append("  or (t.auditor_id in (" + roleIds + ") and t.company_id = '1' and t.department_id = '1' ) ");
            strSql.append("  or (t.auditor_id in (" + roleIds + ") and t.company_id = #{companyId} ) ");
            strSql.append("  or (t.auditor_id in (" + roleIds + ") and t.department_id = #{departmentId}) ");
        }
        // 添加委托信息
        for (SysUser item : delegateList) {
            String processId = "'" + item.getWfProcessId().replace(",", "','") + "'";
            String postIds2 = "'" + item.getPostIds().replace(",", "','") + "'";
            String roleIds2 = "'" + item.getRoleIds().replace(",", "','") + "'";
            String userI2 = "'" + item.getId() + "'";
            String companyId2 = "'" + item.getCompanyId() + "'";
            String departmentId2 = "'" + item.getDepartmentId() + "'";

            strSql.append("  or (t.auditor_id =" + userI2 + " and t.process_id in (" + processId + ") )");

            if (StringUtils.isNotEmpty(item.getPostIds())) {
                strSql.append("  or (t.auditor_id in (" + postIds2 + ") and t.company_id = '1' and t.department_id = '1' and t.process_id in (" + processId + ")  ) ");
                strSql.append("  or (t.auditor_id in (" + postIds2 + ") and t.company_id = " + companyId2 + " AND t.process_id in (" + processId + ") ) ");
                strSql.append("  or (t.auditor_id in (" + postIds2 + ") and t.department_id = " + departmentId2 + " AND t.process_id in (" + processId + ") ) ");
            }

            if (StringUtils.isNotEmpty(item.getRoleIds())) {
                strSql.append("  or (t.auditor_id in (" + roleIds2 + ") and t.company_id = '1' and t.department_id = '1' and t.process_id in (" + processId + ")  ) ");
                strSql.append("  or (t.auditor_id in (" + roleIds2 + ") and t.company_id = " + companyId2 + " and t.process_id in (" + processId + ") ) ");
                strSql.append("  or (t.auditor_id in (" + roleIds2 + ") and t.department_id = " + departmentId2 + " and t.process_id in (" + processId + ") ) ");
            }
        }

        strSql.append(" ) group by t.process_id,t.node_id,t.node_name ) a left join wf_processinstance p on p.id = a.process_id where 1 = 1  and (p.is_finished = 0 or a.task_type = 3) and p.status = 1 ");


        strSql.append(" and ( p.id like #{processinstanceId} ) ");

        Map<String, Object> params = new HashMap<>();
        params.put("auditorId", userId);
        params.put("companyId", companyId);
        params.put("departmentId", departmentId);
        params.put("sql", strSql.toString());
        params.put("processinstanceId", processinstanceId);

        int total = dynamicScriptDao.count(params);
        return total > 0;
    }


    /**
     * 获取委托人信息列表
     *
     * @param userId 用户Id
     * @return
     */

    private List<SysUser> getDelegateTask(String userId) {
        List<SysUser> delegateUserlist = new ArrayList<>();
        Date datatime = new Date();
        Map<String, Object> params = new HashMap<>();
        params.put("toUserid", userId);
        params.put("currentTime", datatime);
        List<WFDelegateRule> wfDelegateRuleList = delegateRuleDao.findList(params);
        for (WFDelegateRule item : wfDelegateRuleList) {
            SysUser userinfo = new SysUser();
            userinfo.setId(item.getCreateUserid());

            List<WFProcessinstance> processinstanceList = processinstanceDao.findByDelegateRuleId(item.getId());

            userinfo.setWfProcessId("");
            for (WFProcessinstance pi : processinstanceList) {
                if (StringUtils.isNotEmpty(userinfo.getWfProcessId())) {
                    userinfo.setWfProcessId(userinfo.getWfProcessId() + ",");
                }
                userinfo.setWfProcessId(userinfo.getWfProcessId() + pi.getId());
            }

            if (StringUtils.isNotEmpty(userinfo.getWfProcessId())) {
                delegateUserlist.add(userinfo);
            }
        }
        return delegateUserlist;
    }


}
